#include "mkvmuxfilter.hpp"
#include <uuids.h>
#include <dvdmedia.h>
#include "mkvstreamvideompeg2.hpp"
//#include "odbgstream.hpp"
//using std::endl;
//using std::boolalpha;

namespace MkvMux
{

InpinVideo::InpinVideo(Filter* p) :
    Inpin(p, L"video")
{
    AM_MEDIA_TYPE mt;
    
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;  //TODO: more subtypes here
    
    mt.bFixedSizeSamples = FALSE;
    mt.bTemporalCompression = FALSE;
    mt.lSampleSize = 0;
    mt.formattype = GUID_NULL;
    mt.pUnk = 0;
    mt.cbFormat = 0;
    mt.pbFormat = 0;
    
    m_preferred.Add(mt);
}


InpinVideo::~InpinVideo()
{
}


HRESULT InpinVideo::QueryAccept(const AM_MEDIA_TYPE* pmt)
{
    if (pmt == 0)
        return E_INVALIDARG;
        
    const AM_MEDIA_TYPE& mt = *pmt;
    
    if (mt.majortype != MEDIATYPE_Video)
        return S_FALSE;
    
    if (mt.subtype != MEDIASUBTYPE_MPEG2_VIDEO)
        return S_FALSE;
        
    if (mt.formattype != FORMAT_MPEG2_VIDEO)
        return S_FALSE;

    //if (mt.cbFormat < sizeof(MPEG2VIDEOINFO))
    //    return S_FALSE;
        
    if (mt.cbFormat < (ULONG)FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader[0]))
        return S_FALSE;

    if (mt.pbFormat == 0)
        return S_FALSE;
        
    const MPEG2VIDEOINFO& mpg = (MPEG2VIDEOINFO&)(*mt.pbFormat);

    if (mt.cbFormat < SIZE_MPEG2VIDEOINFO(&mpg))
        return S_FALSE;

    const VIDEOINFOHEADER2& vih = mpg.hdr;
    const BITMAPINFOHEADER& bmih = vih.bmiHeader;
    bmih;
    
    //TODO: anything else?
    //we have seqhdr
    
    return S_OK;  
}


HRESULT InpinVideo::GetAllocatorRequirements(ALLOCATOR_PROPERTIES* p)
{
    if (p == 0)
        return E_POINTER;
        
    ALLOCATOR_PROPERTIES& props = *p;
    
    //TODO: estimate this value based on value of framerate.
    props.cBuffers = 1;  //we copy payload immediately    
    props.cbBuffer = 0;
    props.cbAlign = 0;
    props.cbPrefix = 0;
    
    return S_OK;
}


HRESULT InpinVideo::OnInit()
{  
   assert(m_connection);
   assert(!m_connection_mt.Empty());

   const AM_MEDIA_TYPE& mt = m_connection_mt[0];
   assert(mt.majortype == MEDIATYPE_Video);
   assert(mt.subtype == MEDIASUBTYPE_MPEG2_VIDEO);  //TODO: more subtypes here
   assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER2));
   assert(mt.pbFormat);  //fully vetted in InpinVideo::QueryAccept

   const MPEG2VIDEOINFO& mpg = (MPEG2VIDEOINFO&)(*mt.pbFormat);
   assert(mt.cbFormat >= SIZE_MPEG2VIDEOINFO(&mpg));

   const DWORD cb = mpg.cbSequenceHeader;
   assert(cb >= 4);

   const VIDEOINFOHEADER2& vih = mpg.hdr;
   const BITMAPINFOHEADER& bmih = vih.bmiHeader;
   bmih;

   //http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html#seq

   const BYTE* const h = MPEG2_SEQUENCE_INFO(&mpg);
   assert(h[0] == 0);
   assert(h[1] == 0);
   assert(h[2] == 1);
   assert(h[3] == 0xB3);
   
   MkvContext& mkv = m_pFilter->m_mkv;

   m_pStream = new (std::nothrow) MkvStreamVideoMpeg2(mkv, h, cb);
   assert(m_pStream);  //TODO

   mkv.SetVideo(m_pStream);   

   return S_OK;
}


void InpinVideo::Final()
{
   MkvContext& ctx = m_pFilter->m_mkv;
   ctx.SetVideo(0);

   delete m_pStream;
   m_pStream = 0;
}


//HRESULT InpinVideo::OnReceive(IMediaSample* pSample)
//{   
//   
//    return m_pFilter->m_outpin.WriteVideo(pSample);
//}
//
//
//HRESULT InpinVideo::OnEndOfStream()
//{
//    return m_pFilter->m_outpin.WriteVideoEOS();
//}


} //end namespace MkvMux
